﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;
using System.Threading;
using System.Threading.Tasks;

using agree.configuration;
using agree.schema;

using alib.Collections.ReadOnly;
using alib.Debugging;
using alib.Enumerable;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// required property init:
	///		FeatMgr
	///		MrsManager
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed partial class EntryMgr : EntryMgrTdlLoader, ISupportInitializeNotification,
		ρρLexicon,
		ρρTokenMappingGeometry
	{
		public EntryMgr(IRtParent parent)
			: base(parent)
		{
			mre_rule_cache = alib.Concurrency.Tasks.Unclaimed;
		}

		public RuleSet _rules;

		public ITokenizer DefaultTokenizer { get; set; }


		/// Aggregate Entry token streams into groups, construct Entries. Release references to the original tokens
		/// as soon as they are no longer needed. In the future, we may wish to retain these to enable live
		/// editing scenarios.
		public void EndInit()
		{
			td.ΔTop.set_top(g);

			load_entry_groups();

			/// Create and persist TFSs for authored definitions
			foreach (var ent in AllEntries)
			{
				var def = ent.Definition;
			}

			TaskControl.ParallelLoop(bcpo.type_arr, t =>
			{
				var exp = t.Expanded;
			});


			Unification.ReleaseSpares();

			ftm.set_maximal_well_formed_types();

			//_root_coverage_report();

			//_dump_all_feature_structures("fs-dump-defs.txt", "fs-dump.txt", "fs-dump-ent.txt");

			//_node_labels.Alloc(AllEntries.OfType<NodeLabel>());
			_node_labels.Freeze();

			_prepare_rules();

			/// Select and expand start symbols. 
			_prepare_start_symbols();

			/// Pre-cache MRSs for valid trigger rules
			_prepare_trigger_rules();

			_prepare_token_mapping_rules();

			_prepare_lexical_filtering_rules();

			f_init_complete = true;

			//__find_inaccessible_types();
		}

		public bool f_init_complete;

		///////////////////////////////////////////////////////////////////////
		///
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public ManualResetEvent mre_rule_cache;
		public void WaitForRemainingInits()
		{
			var _tmp = mre_rule_cache;
			if (_tmp != null && _tmp != alib.Concurrency.Tasks.Unclaimed)
				_tmp.WaitOne();
		}
		///
		///////////////////////////////////////////////////////////////////////


		///////////////////////////////////////////////////////////////////////
		/// 
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public Lexicon lex;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public Lexicon Lexicon
		{
			get { return lex; }
			set { lex = value; }
		}
		/// 
		///////////////////////////////////////////////////////////////////////


		///////////////////////////////////////////////////////////////////////
		///
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		TokenMappingGeometry _tmg;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public TokenMappingGeometry TokenMappingGeometry
		{
			get
			{
				if (_tmg == null)
				{
					Debug.Assert(this.im != null);
					_tmg = new TokenMappingGeometry(this, this.im);
				}
				return _tmg;
			}
			set
			{
				_tmg = value;
				if (_tmg.Isometrics == null)
				{
					Debug.Assert(this.im != null);
					_tmg.Isometrics = this.im;
				}
				else
					Debug.Assert(_tmg.Isometrics == this.im);
			}
		}
		/// 
		///////////////////////////////////////////////////////////////////////


		///////////////////////////////////////////////////////////////////////
		///
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		long _pr_basis;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		SyntaxRule[] _pr_cached;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public SyntaxRule[] ParserRules
		{
			get
			{
				var _tmp = AgreeConfig.Parser.Chart.IgnoreRules;

				long vh = _tmp.VersionHash;
				if (_pr_basis != vh)
				{
					if (_tmp != null && _tmp.Count > 0)
					{
						_pr_cached = _syntax_rules.Except(_tmp.SelectNotNull(s =>
						{
							Entry entry;
							entry_dict.TryGetValue(s, out entry);
							return entry as SyntaxRule;
						}))
						.ToArray();
					}
					else
						_pr_cached = _syntax_rules.__get_arr();

					_pr_basis = vh;
				}
				return _pr_cached;
			}
		}
		public StartSymbol[] ParserStartSymbols { get; set; }
		/// 
		///////////////////////////////////////////////////////////////////////


		///////////////////////////////////////////////////////////////////////
		/// 
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		long _gr_basis;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		Rule[] _gr_cached;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public Rule[] GeneratorRules
		{
			get
			{
				var _tmp = AgreeConfig.Generator.Chart.IgnoreRules;
				long vh = _tmp.VersionHash;
				if (_gr_basis != vh)
				{
					if (_tmp != null && _tmp.Count > 0)
					{
						//var gen_lex_rules = _lexical_rules
						//.Except(gir.SelectNotNull(s => tm.entry_dict.TryGetValue(s) as LexicalRule))
						//.ToArray(_lexical_rules.Length);
						//var gen_grammar_rules = _grammar_rules
						//.Except(gir.SelectNotNull(s => tm.entry_dict.TryGetValue(s) as SyntaxRule))
						//.ToArray(_grammar_rules.Length);

						_gr_cached = _rules.Except(_tmp.SelectNotNull(s =>
						{
							Entry entry;
							entry_dict.TryGetValue(s, out entry);
							return entry as Rule;
						}))
						.ToArray();
					}
					else
						_gr_cached = _rules.ToArray();

					_gr_basis = vh;
				}
				return _gr_cached;
			}
		}
		public StartSymbol[] GeneratorStartSymbols { get; set; }
		/// 
		///////////////////////////////////////////////////////////////////////


		///////////////////////////////////////////////////////////////////////
		/// 
		//readonly TriggerRuleMgr trg_set;
		//public TriggerRuleMgr TriggerRuleMgr
		//{
		//	get { return trg_set; }
		//	set { throw alib.not.valid; }
		//}
		void _prepare_trigger_rules()
		{
			_trigger_rules.Freeze();

			if (_trigger_rules.Count == 0)
				return;

			//int i = 0;
			//foreach (var ent in AllEntries)
			//{
			//	var tr = ent as TriggerRule;
			//	if (tr != null)
			//		_trigger_rules.arr[i++] = tr;
			//}

			//trg_set.initialize();
		}
		/// 
		///////////////////////////////////////////////////////////////////////


		///////////////////////////////////////////////////////////////////////
		/// 
		void _prepare_token_mapping_rules()
		{
			_token_mapping_rules.Freeze();

			if (_token_mapping_rules.Count == 0)
				return;

			//int i = 0;
			foreach (var q in AllEntries.OfType<TokenMappingRule>())
			{
				//q.prepare_tmr_info();
				var exp = q.Expanded;
				//_token_mapping_rules.arr[i++] = q;
			}
		}
		/// 
		///////////////////////////////////////////////////////////////////////


		///////////////////////////////////////////////////////////////////////
		/// 
		void _prepare_lexical_filtering_rules()
		{
			_lexical_filtering_rules.Freeze();

			if (_lexical_filtering_rules.Count == 0)
				return;

			//int i = 0;
			foreach (var q in AllEntries.OfType<LexicalFilteringRule>())
			{
				var exp = q.Expanded;
				//_lexical_filtering_rules.arr[i++] = q;
			}
		}
		/// 
		///////////////////////////////////////////////////////////////////////


		///////////////////////////////////////////////////////////////////////
		/// 
		public StartSymbol gen_NonIdiomRoot;
		public StartSymbol parse_NonIdiomRoot;
		public void _prepare_start_symbols()
		{
			_start_symbols.Freeze();

			StartSymbol[] rg_pss = null, rg_gss = null;

			for (int i = 0; i < _start_symbols.Count; i++)
			{
				var ss = _start_symbols[i];
				var exp = ss.Expanded;

				if (AgreeConfig.Parser.Chart.StartSymbols.Contains(ss.Name, cmp_entry_name))
					alib.Array.arr.Append(ref rg_pss, ss);

				if (AgreeConfig.Generator.Chart.StartSymbols.Contains(ss.Name, cmp_entry_name))
					alib.Array.arr.Append(ref rg_gss, ss);

				/// Non-idiom root conditions

				if (cmp_entry_name.Compare(AgreeConfig.Parser.Chart.NonIdiomRoot, ss.Name) == 0)
					parse_NonIdiomRoot = ss;

				if (cmp_entry_name.Compare(AgreeConfig.Generator.Chart.NonIdiomRoot, ss.Name) == 0)
					gen_NonIdiomRoot = ss;
			}

			if (rg_pss.Length == 0)
				Debug.Print("WARNING: there are no parser start symbols");
			if (rg_gss.Length == 0)
				Debug.Print("WARNING: there are no generator start symbols");

			/// Start symbols must be evaluated in order of appearance as specified by the grammar writer and 
			/// so order must be preserved after the parallel operation.

			ParserStartSymbols = rg_pss;
			GeneratorStartSymbols = rg_gss;
		}
		/// 
		///////////////////////////////////////////////////////////////////////


		///////////////////////////////////////////////////////////////////////
		/// 
		void _prepare_rules()
		{
			foreach (String s in AgreeConfig.Grammar.SpanOnlyRules)
			{
				Entry ent;
				if (!entry_dict.TryGetValue(s, out ent))
					throw new TdlException("Span only rule type '{0}' was not defined in any TDL file.", s);
				var gr = ent as SyntaxRule;
				if (gr == null)
					throw new TdlException("Span only rule type '{0}' is not a grammar rule.", s);
				gr._set_span_only();
				//Debug.Print("span-only: {0}", gr.Name);
			}

			_syntax_rules.Freeze();
			_lexical_rules.Freeze();

			_rules = new RuleSet(this);

			_lexical_entries.Freeze();
			_generic_lexical_entries.Freeze();
		}
		///
		///////////////////////////////////////////////////////////////////////

	};
}
